﻿@page "/socket/data-entity"
@inject IStringLocalizer<DataEntities> Localizer

<h3>@Localizer["DataEntityTitle"]</h3>
<h4>@Localizer["DataEntityDescription"]</h4>

<DemoBlock Title="@Localizer["NormalTitle"]"
           Introduction="@Localizer["NormalIntro"]"
           Name="Normal" ShowCode="false">
    <p>通过 <code>DataPropertyConverterAttribute</code> 属性标签可以将通讯数据自动转化为我们系统中需要的业务实体类，示例如下：</p>
    <Pre>class MockEntity
{
    [DataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
    public byte[]? Header { get; set; }

    [DataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
    public byte[]? Body { get; set; }

    [DataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
    public string? Value1 { get; set; }

    [DataPropertyConverter(Type = typeof(int), Offset = 8, Length = 1)]
    public int Value2 { get; set; }
}</Pre>
    <p class="code-label">1. <code>DataPropertyConverterAttribute</code> 参数说明</p>
    <ul class="ul-demo">
        <li><b>Type</b>: 转换目标数据类型</li>
        <li><b>Offset</b>: 数据偏移量，即在接收到的数据中起始位置</li>
        <li><b>Length</b>: 数据长度，即在接收到的数据中占的长度</li>
        <li><b>EncodingName</b>: 转成字符串时需要的编码名称，默认 null 使用 <code>utf-8</code> 编码</li>
        <li><b>ConverterType</b>: 自定义转化器类型，继承 <code>IDataPropertyConverter</code> 接口</li>
        <li><b>ConverterParameters</b>: 自定义转化器类型构造函数所需的参数，默认 null</li>
    </ul>
    <p>组件库内置了大量数据类型转换器</p>
    <ul class="ul-demo">
        <li><code>DataByteConverter</code> 转成 byte 类型</li>
        <li><code>DataByteArrayConverter</code> 转成 byte[] 数组类型</li>
        <li><code>DataStringConverter</code> 转成 string 字符串类型</li>
        <li><code>DataEnumConverter</code> 转成 enum 枚举类型</li>
        <li><code>DataBoolConverter</code> 转成 bool 布尔类型</li>
        <li><code>DataInt16BigEndianConverter</code> 转成 short 整形大端读取</li>
        <li><code>DataInt32BigEndianConverter</code> 转成 int 整形大端读取</li>
        <li><code>DataInt64BigEndianConverter</code> 转成 long 长整形大端读取</li>
        <li><code>DataSingleBigEndianConverter</code> 转成 float 单精度浮点数大端读取</li>
        <li><code>DataDoubleBigEndianConverter</code> 转成 double 双精度浮点数大端读取</li>
        <li><code>DataUInt16BigEndianConverter</code> 转成 ushort 无符号整形大端读取</li>
        <li><code>DataUInt32BigEndianConverter</code> 转成 uint 无符号整形大端读取</li>
        <li><code>DataUInt64BigEndianConverter</code> 转成 ulong 无符号长整形大端读取</li>
        <li><code>DataInt16LittleEndianConverter</code> 转成 short 整形小端读取</li>
        <li><code>DataInt32LittleEndianConverter</code> 转成 int 整形小端读取</li>
        <li><code>DataInt64LittleEndianConverter</code> 转成 long 长整形小端读取</li>
        <li><code>DataSingleLittleEndianConverter</code> 转成 float 单精度浮点数小端读取</li>
        <li><code>DataDoubleLittleEndianConverter</code> 转成 double 双精度浮点数小端读取</li>
        <li><code>DataUInt16LittleEndianConverter</code> 转成 ushort 无符号整形小端读取</li>
        <li><code>DataUInt32LittleEndianConverter</code> 转成 uint 无符号整形小端读取</li>
        <li><code>DataUInt64LittleEndianConverter</code> 转成 ulong 无符号整形小端读取</li>
    </ul>
    <p>自定义数据类型转化器示例</p>
    <Pre>class MockEntity
{
    [DataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)]
    public byte[]? Header { get; set; }

    [DataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)]
    public byte[]? Body { get; set; }

    [DataPropertyConverter(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
    public string? Value1 { get; set; }
}</Pre>
    <Pre>class FooConverter(string name) : IDataPropertyConverter
{
    public object? Convert(ReadOnlyMemory&lt;byte&gt; data)
    {
        return new Foo() { Id = data.Span[0], Name = name };
    }
}</Pre>
    <p>此处返回值会与参数 <code>Type = typeof(Foo)</code> 做数据合规检查，如果返回值无法转换为指定 <code>Type</code> 时不进行赋值操作，代码逻辑如下</p>
    <Pre>var attr = p.GetCustomAttribute&lt;DataPropertyConverterAttribute&gt;(false);
if (attr is { Type: not null })
{
    var value = attr.ConvertTo(data);
    var valueType = value?.GetType();
    if (value == null && IsNullable(p.PropertyType))
    {
        p.SetValue(entity, null);
    }
    else if (p.PropertyType.IsAssignableFrom(valueType))
    {
        p.SetValue(entity, value);
    }
}</Pre>
    <p>更多技术细节可以参考以上内置提供的转换器源码</p>
</DemoBlock>

<DemoBlock Title="@Localizer["ConfigTitle"]"
           Introduction="@Localizer["ConfigIntro"]"
           Name="Config" ShowCode="false">
    <p>通过 <code>ConfigureDataConverters</code> 可以配置任意类型（第三方程序集中无源码类型）自定数据转换</p>
    <Pre>class MockEntity
{
    public byte[]? Header { get; set; }

    public byte[]? Body { get; set; }
}</Pre>
    <Pre>service.ConfigureDataConverters(options =>
{
    // 配置 MockEntity 属性 Header 转换逻辑 从起始位置 0 开始读取 5 个字节
    options.AddPropertyConverter&lt;MockEntity&gt;(entity => entity.Header, new DataPropertyConverterAttribute()
    {
        Offset = 0,
        Length = 2
    });

    // 配置 MockEntity 属性 Body 转换逻辑 从起始位置 5 开始读取 2 个字节
    options.AddPropertyConverter&lt;MockEntity&gt;(entity => entity.Body, new DataPropertyConverterAttribute()
    {
        Offset = 3,
        Length = 2
    });
});</Pre>
    <p>通过调用 <code>ITcpSocketClient</code> 实例泛型扩展方法 <code>AddDataPackageAdapter</code> 在回调方法 <code>OnReceiveAsync</code> 中直接拿到数据实体类</p>
    <Pre>// 创建一个数据适配器
var adapter = new DataPackageAdapter(new FixLengthDataPackageHandler(7));

// 设置数据适配器与回调方法
_client.AddDataPackageAdapter&lt;MockEntity&gt;(adapter, OnReceiveAsync);</Pre>
    <Pre>private async Task OnReceiveAsync(MockEntity? entity)
{
    return Task.CompletedTask;
}</Pre>
</DemoBlock>
